package com.koron.plan.web.repairplan.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.alibaba.fastjson.JSON;
import com.koron.bean.enums.TimePeriod;
import com.koron.bean.system.staff.vo.EamUser;
import com.koron.bean.workflow.RedirectQuery;
import com.koron.common.core.business.system.feign.SystemFeignService;
import com.koron.common.core.business.workflow.templates.WorkFlowHandler;
import com.koron.common.core.config.ThreadLocalContext;
import com.koron.common.core.util.CodeTools;
import com.koron.common.core.util.OrgCacheUtils;
import com.koron.common.task.web.entity.CronTask;
import com.koron.common.task.web.entity.XxlJobInfo;
import com.koron.common.task.web.service.XxlJobService;
import com.koron.plan.config.Constants;
import com.koron.plan.web.common.CommonUtils;
import com.koron.plan.web.feign.EquipmentFeign;
import com.koron.plan.web.feign.OrderFeign;
import com.koron.plan.web.feign.dto.EquipmentParams;
import com.koron.plan.web.maintainplan.bean.convertor.MaintainConvertor;
import com.koron.plan.web.maintainplan.bean.convertor.MaintainPlanDispatchConvertor;
import com.koron.plan.web.maintainplan.bean.convertor.MaintainPlanStandardConvertor;
import com.koron.plan.web.maintainplan.bean.dto.OrderDispatchDto;
import com.koron.plan.web.maintainplan.bean.dto.OrderEquipPo;
import com.koron.plan.web.maintainplan.bean.entity.*;
import com.koron.plan.web.maintainplan.bean.vo.MaintainPlanDetailVO;
import com.koron.plan.web.maintainplan.bean.vo.MaintainPlanDispatchDetailVO;
import com.koron.plan.web.maintainplan.bean.vo.MaintainPlanStandardVO;
import com.koron.plan.web.maintainplan.bean.vo.MaintainPlanVO;
import com.koron.plan.web.maintainplan.mapper.MaintainPlanMapper;
import com.koron.plan.web.maintainplan.service.*;
import com.koron.plan.web.repairplan.bean.dto.ExperimentDTO;
import com.koron.plan.web.repairplan.bean.dto.OverhaulOrderBean;
import com.koron.plan.web.repairplan.bean.dto.RepairDTO;
import com.koron.plan.web.repairplan.bean.entity.ExperimentEquipmentPo;
import com.koron.plan.web.repairplan.bean.entity.ExperimentReadyPo;
import com.koron.plan.web.repairplan.bean.entity.PlanPackageEquipmentBean;
import com.koron.plan.web.repairplan.bean.entity.PlanPackageIssueBean;
import com.koron.plan.web.repairplan.bean.query.PlanPackageIssueQuery;
import com.koron.plan.web.repairplan.bean.vo.PlanPackageIssueVO;
import com.koron.plan.web.repairplan.mapper.PlanPackageIssueMapper;
import com.koron.plan.web.repairplan.notified.RepairPlanNotifyHolder;
import com.koron.plan.web.repairplan.service.PlanPackageEquipmentService;
import com.koron.plan.web.repairplan.service.PlanPackageIssueService;
import com.xxl.job.core.context.XxlJobHelper;
import com.xxl.job.core.handler.annotation.XxlJob;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.koron.ebs.mybatis.SessionFactory;
import org.koron.ebs.mybatis.TaskAnnotation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import javax.servlet.http.HttpServletRequest;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author twcao
 * @title: PlanPackageIssueServiceImpl
 * @projectName GDH_EAM_V2
 * @description: 保存工单计划service实现
 * @date 2021/9/1314:11
 */
@Slf4j
@Service
public class PlanPackageIssueServiceImpl implements PlanPackageIssueService {

    @Autowired
    private RepairPlanNotifyHolder notifyHolder;

    @Autowired
    private PlanPackageEquipmentService equipmentService;

    @Autowired
    private OrgCacheUtils orgCacheUtils;

    @Autowired
    private EquipmentFeign equipmentFeign;

    @Autowired
    private OrderFeign orderFeign;

    @Autowired
    private SystemFeignService systemFeignService;

    @Autowired
    private WorkFlowHandler workFlowHandler;

    @Autowired
    private XxlJobService xxlJobService;

    @Autowired
    private MaintainPlanReadyService readyService;

    @Autowired
    private MaintainPlanStandardService standardService;

    @Autowired
    private MaintainPlanInstructionService instructionService;

    @Autowired
    private MaintainPlanDispatchService dispatchService;

    @Autowired
    private MaintainPlanDispatchWorkerService dispatchWorkerService;

    @Autowired
    private MaintainPlanDispatchDeptService workDeptService;

    @Autowired
    private MaintainPlanStandardConvertor standardConvertor;

    @Autowired
    private MaintainPlanDispatchConvertor dispatchConvertor;

    @Autowired
    private PlanEquipService planEquipService;

    @Autowired
    private MaintainPlanService maintainPlanService;

    @Autowired
    private MaintainConvertor maintainConvertor;
    @Autowired
    private HttpServletRequest httpServletRequest;
    @Override
    @TaskAnnotation("queryById")
    public PlanPackageIssueVO queryById(SessionFactory factory, String id) {
        Assert.hasText(id, "计划id不能为空");
        PlanPackageIssueMapper issueMapper = factory.getMapper(PlanPackageIssueMapper.class);
        PlanPackageIssueVO vo = issueMapper.queryById(id);
        return vo;
    }

    /**
     * 保存或更新
     * @param factory 会话
     * @param beans   实例
     * @return
     */
    @Override
    public boolean saveOrUpdate(SessionFactory factory, List<PlanPackageIssueBean> beans) {
        if(CollectionUtils.isEmpty(beans)) {
            return true;
        }
        PlanPackageIssueMapper issueMapper = factory.getMapper(PlanPackageIssueMapper.class);
        beans.stream().forEach(row -> {
            row.setId(StringUtils.isBlank(row.getId()) ? CodeTools.getCode32() : row.getId());
            row.setDeptManageName(orgCacheUtils.getOrgNameById(row.getDeptManage()));
            row.setOrganizerName(orgCacheUtils.getOrgNameById(row.getOrganizer()));
            row.setOrgName(orgCacheUtils.getOrgNameById(row.getOrgId()));
        });
        return issueMapper.saveOrUpdate(beans);
    }

    /**
     * 保存或更新
     *
     * @param factory
     * @param vo
     * @return
     */
    @Override
    @TaskAnnotation("saveOrUpdate")
    public PlanPackageIssueVO saveOrUpdate(SessionFactory factory, PlanPackageIssueVO vo) {
        EamUser currUser = ThreadLocalContext.get();
        CommonUtils.update(vo, currUser);
        vo.setId(StringUtils.isBlank(vo.getId()) ? CodeTools.getCode32() : vo.getId());
        vo.setPlanCode(StringUtils.isBlank(vo.getIssueCode()) ? CommonUtils.getCodeNo(Constants.ISSUE_JOB_TYPE.prefixMap.get(vo.getJobType()), 3) : vo.getIssueCode());
        vo.setStatus(vo.getStatus() == null ? Constants.REPAIR_PACKAGE_ISSUE_STATE.DRAFT : vo.getStatus());
        // 检修计划审批时，需要用到创建人所在单位
        vo.setOrgId(currUser.getOrgId());
        // 工单计划
        saveOrUpdate(factory, Arrays.asList(vo));
        // 工单计划设备
        List<PlanPackageEquipmentBean> equipments = vo.getEquipments();
        CommonUtils.sorted(equipments);
        equipments.stream().forEach(equipment -> {
            equipment.setId(StringUtils.isBlank(equipment.getId()) ? CodeTools.getCode32() : equipment.getId());
            equipment.setPackageIssueId(vo.getId());
        });
        equipmentService.deleteByIssueId(factory, vo.getId());
        equipmentService.saveOrUpdate(factory, equipments);
        return vo;
    }

    /**
     * 根据计划包id删除工单计划
     * @param factory
     * @param packageId 计划包id
     * @return
     */
    @Override
    @TaskAnnotation("deleteByPackageId")
    public boolean deleteByPackageId(SessionFactory factory, String packageId) {
        Assert.hasText(packageId, "计划包id不能为空");
        PlanPackageIssueMapper issueMapper = factory.getMapper(PlanPackageIssueMapper.class);
        // 删除计划包的计划
        issueMapper.deleteByPackageId(packageId);
        List<PlanPackageIssueVO> issueVOS = issueMapper.queryList(new PlanPackageIssueQuery().setPackageId(packageId));
        // 删除计划包内计划的工作流
        issueVOS.stream().map(PlanPackageIssueVO::getIssueId).forEach(row -> deleteById(factory, row));
        return true;
    }

    /**
     * 根据计划id删除
     *
     * @param factory
     * @param issueId
     * @return
     */
    @Override
    @TaskAnnotation("deleteById")
    public boolean deleteById(SessionFactory factory, String issueId) {
        Assert.hasText(issueId, "计划id不能为空");
        PlanPackageIssueMapper issueMapper = factory.getMapper(PlanPackageIssueMapper.class);
        int row = issueMapper.deleteById(issueId);
        // 删除计划的工作流
        workFlowHandler.delete(issueId);
        return row > 0;
    }

    /**
     * 通过实体作为筛选条件查询
     * @param query 实例对象
     * @return 对象列表
     */
    @Override
    @TaskAnnotation("queryList")
    public List<PlanPackageIssueVO> queryList(SessionFactory factory, PlanPackageIssueQuery query) {
        PlanPackageIssueMapper issueMapper = factory.getMapper(PlanPackageIssueMapper.class);
        List<PlanPackageIssueVO> issueVOS = issueMapper.queryList(query);

        //嵌套list分页查询,改造  两种方式,自己分页/从表单独查询  暂时采用第二种

        if (CollectionUtils.isNotEmpty(issueVOS)) {
            List<String> issueIds = issueVOS.stream().map(PlanPackageIssueVO::getId).collect(Collectors.toList());
            List<PlanPackageEquipmentBean> planPackageEquipmentBeans = equipmentService.queryListByIssueIds(factory, issueIds);
            if (CollectionUtils.isNotEmpty(planPackageEquipmentBeans)) {
                Map<String, List<PlanPackageEquipmentBean>> issueEquipMap = planPackageEquipmentBeans.stream().collect(Collectors.groupingBy(PlanPackageEquipmentBean::getPackageIssueId));
                issueVOS.forEach(p -> {
                    p.setEquipments(issueEquipMap.get(p.getId()));
                });
            }
        }

        return issueVOS;
    }

    /**
     * 下发计划
     * @param factory 会话
     * @param issueId 计划id
     * @return
     */
    @Override
    @TaskAnnotation("issued")
    public PlanPackageIssueBean issued(SessionFactory factory, String issueId) {
        Assert.hasText(issueId, "计划id必填");
        PlanPackageIssueMapper issueMapper = factory.getMapper(PlanPackageIssueMapper.class);
        PlanPackageIssueQuery query = new PlanPackageIssueQuery();
        query.setId(issueId);
        List<PlanPackageIssueVO> issueVOS = queryList(factory, query);
        Assert.isTrue(CollectionUtils.size(issueVOS) == 1, "计划id不存在");
        PlanPackageIssueVO issue = issueVOS.get(0);
        // 获取主办部门
        PlanPackageIssueBean bean = issueMapper.queryById(issueId);
        // 注意： 调用工单生成接口，拿到工单编号，存于w_plan_package_issue.issue_id、issue_code字段
        // 在生成的计划工单中添加相应的计划id和计划code
        switch (bean.getJobType()) {
            case Constants.ISSUE_JOB_TYPE.OVERHAUL:
                // 创建大修工单对象
                PlanPackageIssueVO overhaulOrder = createOverhaulOrder(factory, issue);
                bean.setIssueId(overhaulOrder.getIssueId());
                bean.setIssueCode(overhaulOrder.getIssueCode());
                break;
            case Constants.ISSUE_JOB_TYPE.EXPERIMENT:
                // 创建试验工单对象
                PlanPackageIssueVO experimentOrder = createExperimentOrder(factory, issue);
                bean.setIssueId(experimentOrder.getIssueId());
                bean.setIssueCode(experimentOrder.getIssueCode());
                break;
            case Constants.ISSUE_JOB_TYPE.REPAIR:
                // 创建计划性检修工单对象
                PlanPackageIssueVO repairOrder = createRepairOrder(factory, issue);
                bean.setIssueId(repairOrder.getIssueId());
                bean.setIssueCode(repairOrder.getIssueCode());
        }
        // 状态设置为已下发
        bean.setStatus(Constants.REPAIR_PACKAGE_ISSUE_STATE.ISSUED);
        saveOrUpdate(factory, Arrays.asList(bean));
        notifyHolder.saveAfter(factory, bean);
        return bean;
    }

    /**
     * 工单状态变化后，调用此接口
     * @param factory   会话
     * @param issueCode 工单编号
     * @param issueState 工单状态
     * @return
     */
    @Override
    @TaskAnnotation("issueStateChange")
    public PlanPackageIssueBean issueStateChange(SessionFactory factory, String issueCode, Integer issueState) {
        Assert.hasText(issueCode, "工单编号不能为空");
        PlanPackageIssueMapper issueMapper = factory.getMapper(PlanPackageIssueMapper.class);
        List<PlanPackageIssueBean> issueList = issueMapper.getByIssueCode(issueCode);
        Assert.isTrue(CollectionUtils.size(issueList) == 1, CollectionUtils.isEmpty(issueList) ? "工单不存在" : "工单存在多个");
        PlanPackageIssueBean bean = issueList.get(0);
        bean.setStatus(issueState);
        saveOrUpdate(factory, Arrays.asList(bean));
        notifyHolder.saveAfter(factory, bean);
        return bean;
    }

    /**
     * 工作流更新
     *
     * @param factory
     * @param redirectQuery
     */
    @Override
    @TaskAnnotation("workflowUpdate")
    public Boolean workflowUpdate(SessionFactory factory, RedirectQuery redirectQuery) {
        Integer operation = redirectQuery.getOperation();
        PlanPackageIssueVO vo = JSON.parseObject(redirectQuery.getFormVariables(), PlanPackageIssueVO.class);
        switch (operation) {
            case 256: // 撤回
            case 1: // 驳回
                // 驳回的节点编号
                // 驳回到草稿节点
                vo.setStatus(Constants.REPAIR_PACKAGE_ISSUE_STATE.DRAFT);
                saveOrUpdate(factory, vo);
                break;
            case 32: // 同意
                vo.setStatus(Constants.REPAIR_PACKAGE_ISSUE_STATE.WAIT_REVIEW);
                saveOrUpdate(factory, vo);
        }
        return true;
    }

    /**
     * 生成试验工单
     * @param factory 会话
     * @param detailVO 保存的实体
     * @return
     */
    @Override
    @TaskAnnotation("saveOrUpdateExperiment")
    public MaintainPlanDetailVO saveOrUpdateExperiment(SessionFactory factory, MaintainPlanDetailVO detailVO) {
        // 1、初始化
        // 1.1、设置主键
        detailVO.setId(StringUtils.isBlank(detailVO.getId()) ? CodeTools.getCode32() : detailVO.getId());
        // 1.2、设置删除标志
        CommonUtils.update(detailVO);
        // 1.3、生成流程业务id
        detailVO.setBillId(StringUtils.isBlank(detailVO.getBillId()) ? CodeTools.getCode32() : detailVO.getBillId());

        // 设置cron表达式
        if(Constants.YesOrNo.YES.equals(detailVO.getIsTimer())) {
            detailVO.getCronTask().setCron(CommonUtils.parseStrToCron(detailVO.getCronTask()));
        }
        // 1.3、设置计划状态， 默认为草稿状态
        detailVO.setStatus(detailVO.getStatus() == null ? Constants.MAINTAIN_PLAN_STATE.DRAFT : detailVO.getStatus());
        detailVO.setCode(StringUtils.isBlank(detailVO.getCode()) ? CommonUtils.getCodeNo(Constants.EXPERIMENT_PLAN_PREFIX, 3) : detailVO.getCode());
        // 2、参数合法性校验
        // 2.1、只有草稿状态的试验计划才可以修改
        // Assert.isTrue(Constants.MAINTAIN_PLAN_STATE.DRAFT.equals(detailVO.getStatus()), "请先停用试验计划");
        // 3、试验计划基础
        MaintainPlanMapper planMapper = factory.getMapper(MaintainPlanMapper.class);
        detailVO.setDeptName(orgCacheUtils.getOrgNameById(detailVO.getDeptId()));
        detailVO.setDeptManageName(orgCacheUtils.getOrgNameById(detailVO.getDeptManage()));
        Assert.isTrue(planMapper.saveOrUpdate(detailVO), "试验计划保存失败");
        // 4、作业准备
        List<MaintainPlanReadyBean> worksReadys = detailVO.getWorkReadys();
        if(!org.springframework.util.CollectionUtils.isEmpty(worksReadys)) {
            // 设置排序值sort
            CommonUtils.sorted(worksReadys);
            worksReadys.stream().forEach(row -> row.setPlanId(detailVO.getId()));
            readyService.deleteByPlanId(factory, detailVO.getId());
            Assert.isTrue(readyService.saveOrUpdate(factory, worksReadys), "作业准备保存失败");
        }
        // 5、作业过程(作业标准)
        List<MaintainPlanStandardVO> standards = detailVO.getStandards();
        if(!org.springframework.util.CollectionUtils.isEmpty(standards)) {
            // 设置排序值sort
            CommonUtils.sorted(standards);
            standards.stream().forEach(row -> row.setPlanId(detailVO.getId()));
            standardService.deleteByPlanId(factory, detailVO.getId());
            Assert.isTrue(standardService.saveOrUpdate(factory, standardConvertor.vosToBeans(standards)), "作业过程保存失败");
        }
        // 6、作业指导书，如果为empty，则表明清空指导书，如果为null，则不做操作
        List<MaintainPlanInstructionBean> instructions = detailVO.getInstructions();
        if(instructions != null) {
            // 设置排序值sort
            CommonUtils.sorted(instructions);
            instructions.stream().forEach(row -> row.setPlanId(detailVO.getId()));
            instructionService.deleteByPlanId(factory, detailVO.getId());
            Assert.isTrue(instructionService.saveOrUpdate(factory, instructions), "作业指导书保存失败");
        }
        // 7、派工信息
        MaintainPlanDispatchDetailVO dispatch = detailVO.getDispatch();
        if(Constants.YesOrNo.YES.equals(detailVO.getIsDispatch())) { // 派工信息可为空
            Assert.notNull(dispatch, "派工信息不能为空");
            dispatch.setPlanId(detailVO.getId());
            dispatch.setId(StringUtils.isBlank(dispatch.getId()) ? CodeTools.getCode32() : dispatch.getId());
            dispatchService.deleteByPlanId(factory, detailVO.getId());
            Assert.isTrue(dispatchService.saveOrUpdate(factory, dispatchConvertor.detailVOToBean(detailVO.getDispatch())), "派工信息保存失败");
            // 7.1、工作班成员
            List<MaintainPlanDispatchWorkerBean> workers = dispatch.getWorkers();
            dispatchWorkerService.deleteByPlanId(factory, detailVO.getId());
            if(!org.springframework.util.CollectionUtils.isEmpty(workers)) {
                // 设置排序值sort
                CommonUtils.sorted(workers);
                workers.stream().forEach(row -> {
                    row.setPlanId(detailVO.getId());
                    row.setDispatchId(dispatch.getId());
                    row.setTeamId(CodeTools.getCode32());
                });
                Assert.isTrue(dispatchWorkerService.saveOrUpdate(factory, workers), "工作班组保存失败");
            }
            // 7.2、工作班工作单位
            List<MaintainPlanDispatchDept> workerDept = dispatch.getWorkerDept();
            workDeptService.deleteByPlanId(factory, detailVO.getId());
            if(!org.springframework.util.CollectionUtils.isEmpty(workerDept)) {
                CommonUtils.sorted(workerDept);
                workerDept.stream().forEach(row -> {
                    row.setPlanId(detailVO.getId());
                    row.setDispatchId(dispatch.getId());
                });
                Assert.isTrue(workDeptService.saveOrUpdate(factory, workerDept), "工作班组工作单位保存失败");
            }
        }
        // 8、设置定时任务表达式(detailVO.getIsTimer() = 1时，cron不能为空)
        if(Constants.YesOrNo.YES.equals(detailVO.getIsTimer())) {
            // 定时下达 = true，新增/更新任务
            Assert.notNull(detailVO.getCronTask(), "定时任务不能为空");
            List<String> executeTimesNum = xxlJobService.nextTriggerTime(detailVO.getCronTask().getCron(), 2);
            Assert.notEmpty(executeTimesNum, "请确认定时任务设定的时间,修改后保存重试");

            CronTask cronTask = xxlJobService.getCronTaskById(detailVO.getId());
            if(cronTask != null) {
                // 更新任务
                xxlJobService.update(detailVO.getId(), detailVO.getCronTask().getCron(), factory.getDbEnv(), null, detailVO.getUpdateBy(), null);
            } else {
                // 新增任务 创建人名字[时间]试验计划
                String jobName = "试验计划[" + detailVO.getCode() + "]";
                xxlJobService.addSimpleJob(detailVO.getId(), detailVO.getCronTask().getCron(), factory.getDbEnv(), jobName, detailVO.getUpdateBy(), "generateExperimentOrder", 30);
            }
        } else {
            // 如果不是定时任务，则删除任务（之前为定时任务时，需要删除）
            xxlJobService.remove(detailVO.getId());
        }
        // 9、计划关联的设备
        List<PlanEquipment> equipments = detailVO.getEquipments();
        if(!org.springframework.util.CollectionUtils.isEmpty(equipments)) {
            // 如果关联了设备，则先删除原先的，再添加传入的
            planEquipService.deleteByPlanId(factory, detailVO.getId());
            equipments.stream().forEach(row -> row.setPlanId(detailVO.getId()));
            planEquipService.saveOrUpdate(factory, equipments);
        }
        return maintainPlanService.queryById(factory, detailVO.getId(), null);
    }

    /**
     * 下发试验计划，生成工单
     * @param factory 会话
     * @param id      试验计划id
     * @return
     */
    @Override
    @TaskAnnotation("issue")
    public MaintainPlanDetailVO issue(SessionFactory factory, String id) {
        MaintainPlanMapper mapper = factory.getMapper(MaintainPlanMapper.class);
        MaintainPlanDetailVO detailVO = mapper.queryDetailById(id,httpServletRequest.getHeader("Language"));
        Assert.isTrue(!Constants.MAINTAIN_PLAN_STATE.DRAFT.equals(detailVO.getStatus()), "计划处于草稿状态，无法下发");
        log.info("手动下发试验计划，生成工单");
        detailVO.setDeptManageName(orgCacheUtils.getOrgNameById(detailVO.getDeptManage()));
        voWrapper(detailVO);
        boolean flag = generateExperimentOrder(detailVO);
        Assert.isTrue(flag, "试验计划生成失败");
        return detailVO;
    }

    /**
     * 生成日常维护工单
     */
    @XxlJob(value = "generateExperimentOrder")
    public boolean generateExperimentOrder(MaintainPlanDetailVO detailVO) {
        // 默认创建失败
        boolean flag = false;
        // 任务类型，1->手动任务，2->定时任务运，不提前，3->定时任务，提前
        Integer taskType = null;
        CronTask cronTask = null;
        // 1、获取任务详情
        if(detailVO != null) {
            // 1、手动生成
            taskType = 1;
        } else {
            // 2、定时任务
            try (SessionFactory factory = new SessionFactory()) {
                cronTask = xxlJobService.getCronTaskByXxlJobId((int) XxlJobHelper.getJobId());
                // 如果是定时任务，则获取的默认数据源为system，需要进行切换
                EamUser user = ThreadLocalContext.get();
                user = user == null ? new EamUser() : user;
                user.setCurrDs(cronTask.getMark());
                ThreadLocalContext.set(user);
                Assert.notNull(cronTask, "定时任务为空");
                if(CommonUtils.parse(cronTask.getBusinessId()) > 0) {
                    // 2.1、定时任务-提前（临时任务，将临时任务id作为业务id查询出定时任务）
                    XxlJobHelper.log("定时任务-提前");
                    taskType = 3;
                    cronTask = xxlJobService.getCronTaskByXxlJobId(CommonUtils.parse(cronTask.getBusinessId()));
                } else {
                    // 2.2、定时任务
                    XxlJobHelper.log("定时任务");
                    taskType = 2;
                }
                factory.setDbEnv(cronTask.getMark());
                XxlJobHelper.log("mark = {}", cronTask.getMark());
                detailVO = maintainPlanService.queryById(factory, cronTask.getBusinessId(), null);
                Assert.notNull(detailVO, "试验计划详情不能为空");
            } catch (Exception e) {
                log.error(e.getMessage(), e);
            }
        }
        log.info("试验计划详情: {}", JSON.toJSONString(detailVO));
        XxlJobHelper.log("试验计划定时任务类型： {}， 试验计划详情: {}", taskType == 1 ? "手动任务" : (taskType == 2 && detailVO.getDelayDays() == 0 ? "定时任务[不提前]" : "定时任务[提前]" + detailVO.getDelayDays() + "天"), JSON.toJSONString(detailVO));
        if(taskType == 1 || (taskType == 2 && !CommonUtils.isPositive(detailVO.getDelayDays())) || (taskType == 3 && CommonUtils.isPositive(detailVO.getDelayDays()))) {
            // 2、调用工单方法生成工单
            try {
                XxlJobHelper.log("定时任务调用工单方法生成工单,taskType:"+taskType);
                ExperimentDTO experimentDTO = createExperimentOrder(detailVO, taskType);
                log.info("试验计划生成工单, dto = {}", JSON.toJSONString(experimentDTO));
                if(Constants.YesOrNo.YES.equals(detailVO.getIsDispatch())) {
                    // 如果isDispatch=true，则直接提交到"待派工"状态
                    experimentDTO.setIsCommit(true);
                }
                String orderId = CommonUtils.ok(orderFeign.insertOrUpdateExperiment(experimentDTO));
                flag = StringUtils.isBlank(orderId) ? false : true;
                XxlJobHelper.log("试验工单插入情况:"+flag);
                log.info("试验计划生成工单响应[派工 = {}], response = {}", experimentDTO.getIsCommit(), orderId);
                if(Constants.YesOrNo.YES.equals(detailVO.getIsDispatch())) {
                    // 如果isDispatch=true，保存派工信息，并提交到待执行状态
                    MaintainPlanDispatchDetailVO dispatch = detailVO.getDispatch();
                    OrderDispatchDto orderDispatchDto = maintainConvertor.dispatchToDispatchDTO(dispatch);
                    orderDispatchDto.setOrderId(orderId);
                    orderDispatchDto.setOrderType(Constants.OrderType.EXPERIMENT);
                    if (dispatch.getIsOut() == 1){
                        orderDispatchDto.setLeader(dispatch.getTrackLeader());
                        orderDispatchDto.setLeaderName(dispatch.getTrackLeaderName());
                    }
                    orderDispatchDto.getOrderDispatchWorker().stream().forEach(row -> row.setOrderId(orderId));
                    orderDispatchDto.getOrderWorkDeptPoList().stream().forEach(row -> {
                        row.setOrderId(orderId);
                        row.setOrderType(Constants.OrderType.EXPERIMENT);
                    });
                    // 正常维护工单，提交到"待执行"状态
                    orderDispatchDto.setIsCommit(true);
                    String id = CommonUtils.ok(orderFeign.saveDispatchInfoExperiment(orderDispatchDto));
                    flag = StringUtils.isBlank(id) ? false : true;
                    log.info("保存派工信息, response = {}", id);
                }
            } catch (Exception e) {
                XxlJobHelper.log(e.getMessage(), e);
                log.error(e.getMessage(), e);
                flag = false;
            }
        }
        // 3、如果是提前执行的任务，调用完工单后，删除当前任务，再生成下次执行的定点任务
        if(taskType == 3) {
            // 3.1、更新下次执行的定点任务
            Date firstTaskTime = getFirstExecuteTimes(detailVO.getCronTask().getCron(), detailVO.getDelayDays());
            String executeTCron = CommonUtils.getCron(firstTaskTime);
            XxlJobInfo jobInfo = xxlJobService.getXxlJobInfoById(cronTask.getXxlJobId());
            XxlJobHelper.log("jobInfo = {}, cronTask = {},detail = {}", JSON.toJSONString(jobInfo), JSON.toJSONString(cronTask), JSON.toJSONString(detailVO));
            xxlJobService.update(String.valueOf(cronTask.getXxlJobId()), executeTCron, cronTask.getMark(), "jobId=" + cronTask.getXxlJobId() + "的临时一次性任务", detailVO.getUpdateBy(), jobInfo.getExecutorHandler());
            xxlJobService.start(String.valueOf(cronTask.getXxlJobId()));
            XxlJobHelper.log("下一次临时一次性任务更新完成, 时间 = {}", executeTCron);
        }
        if(taskType == 2 && CommonUtils.isPositive(detailVO.getDelayDays())) {
            // 临时任务cronTask
            CronTask scheduleTask = xxlJobService.getCronTaskById(String.valueOf(cronTask.getXxlJobId()));
            //临时任务 xxljob
            XxlJobInfo jobInfo = xxlJobService.getXxlJobInfoById(scheduleTask.getXxlJobId());
            if(jobInfo.getTriggerStatus() == 0) {
                Date firstTaskTime = getFirstExecuteTimes(detailVO.getCronTask().getCron(), detailVO.getDelayDays());
                String executeTCron = CommonUtils.getCron(firstTaskTime);
                XxlJobHelper.log("[自救]jobInfo = {}, cronTask = {},detail = {}", JSON.toJSONString(jobInfo), JSON.toJSONString(cronTask), JSON.toJSONString(detailVO));
                xxlJobService.update(String.valueOf(cronTask.getXxlJobId()), executeTCron, cronTask.getMark(), "jobId=" + cronTask.getXxlJobId() + "的临时一次性任务", detailVO.getUpdateBy(), jobInfo.getExecutorHandler());
                xxlJobService.start(String.valueOf(cronTask.getXxlJobId()));
                XxlJobHelper.log("[自救]下一次临时一次性任务更新完成, 时间 = {}", executeTCron);
            }
        }
        XxlJobHelper.log("试验计划生成-执行结果 = {}", flag);
        return flag;
    }

    /**
     * 创建日常试验工单
     */
    public ExperimentDTO createExperimentOrder(MaintainPlanDetailVO detailVO, Integer taskType) {

        ExperimentDTO dto = new ExperimentDTO();
        dto.setDeptManage(detailVO.getDeptManage());
        dto.setDeptManageName(detailVO.getDeptManageName());
        dto.setMajor(detailVO.getMajor());
        dto.setNature(detailVO.getNature());
        dto.setSourceId(Constants.OrderSource.PLAN_ORDER);
        dto.setSource(Constants.OrderSource.orderSourceMap.get(dto.getSourceId()));
        // 工作地点id和名称
        dto.setLocation(detailVO.getAddressId());
        dto.setLocationName(detailVO.getAddress());
        // 计划开始时间和结束时间
        Date firstTime = null;
        if(Constants.YesOrNo.YES.equals(detailVO.getIsTimer())) {
            //如果是定时下发  即taskType=2时,此时获取的下一次触发时间是下一次,应该要取当次才行
            // 例如 每月1号 9点 工期2天,此时到了1月1号的九点,获取的下一次执行时间是 2月1号 实际期望 是此时
            if(taskType==2){
                firstTime = new Date();
            }else {
                firstTime = getFirstExecuteTimes(detailVO.getCronTask().getCron(), 0.0);
            }
            Assert.notNull(firstTime, "工单开始时间已过，请调整定时器后重试");
        }
        int days = CommonUtils.periodToHour(detailVO.getTimeNum(), TimePeriod.parse(detailVO.getTimePeriod()));
        Date[] orderTimes = CommonUtils.computeOrderTime(taskType, days, firstTime);
        // 设置标题生成规则
        dto.setTitle(detailVO.getDeptManageName() + detailVO.getContent());
        dto.setStartTime(orderTimes[0]);
        dto.setEndTime(orderTimes[1]);
        dto.setContext(detailVO.getContent());
        dto.setMarks(detailVO.getDesc());
        dto.setNeedStatus(detailVO.getIsNeed());
        // isCommit ? "待派工" : "草稿"
        dto.setIsCommit(false);
        dto.setCreateBy(detailVO.getCreateBy());
        dto.setCreateByName(detailVO.getCreateByName());
        dto.setCreateTime(new Date());
        dto.setUpdateBy(detailVO.getUpdateBy());
        dto.setUpdateByName(detailVO.getUpdateByName());
        dto.setUpdateTime(new Date());
        dto.setDeptId(detailVO.getDeptId());
        dto.setDeptName(detailVO.getDeptName());
        // 设置计划id和计划code
        dto.setPlanId(detailVO.getId());
        dto.setPlanCode(detailVO.getCode());
        dto.setExperimentReadyPoList((List<ExperimentReadyPo>) convertor(detailVO.getWorkReadys(),ExperimentReadyPo.class));
        dto.setExperimentEquipmentPoList((List<ExperimentEquipmentPo>) convertorToStandards(detailVO.getStandards()));
        dto.setOrderInstructionBeanList(maintainConvertor.instructsToInstructs(detailVO.getInstructions()));
        dto.setOrderEquipPoList(maintainConvertor.equipsToEquips(detailVO.getEquipments()));
        return dto;
    }

    private List convertorToStandards(List<MaintainPlanStandardVO> standards) {
        if (CollectionUtils.isEmpty(standards)){
            return new ArrayList<>();
        }
        List<ExperimentEquipmentPo> standardList = new ArrayList<>(standards.size());
        for (MaintainPlanStandardVO standard : standards) {
            ExperimentEquipmentPo experimentEquipmentPo = new ExperimentEquipmentPo();
            experimentEquipmentPo.setEquipmentId(standard.getEquipmentId());
            experimentEquipmentPo.setEquipmentName(standard.getEquipmentName());
            experimentEquipmentPo.setStandardId(standard.getWorkId());
            experimentEquipmentPo.setStandardName(standard.getWorkName());
            experimentEquipmentPo.setSort(standard.getSort());
            standardList.add(experimentEquipmentPo);
        }
        return standardList;
    }


    private List<?> convertor(List<?> list, Class<?> targetClass) {
        return list.stream().map(l -> {
                    Object obj = BeanUtil.copyProperties(l, targetClass);
//                    try {
//                        Field field = obj.getClass().getDeclaredField("id");
//                        field.setAccessible(true);
//                        field.set(obj,null);
//                    } catch (Exception e) {
//                        log.error("找不到id字段");
//                    }
                    return obj;
                }
        ).collect(Collectors.toList());
    }

    /**
     * 获取提前n天，第一个需要执行的任务（即提前n天，执行时间仍晚于当前时间的时间）
     * @param cron cron表达式
     * @return
     */
    private Date getFirstExecuteTimes(String cron, Double days) {
        int time = 2;
        // 下次执行的时间
        Date nextTime = null;
        while (nextTime == null || nextTime.compareTo(new Date()) <= days * 24 * 3600 * 1000 || time > 1024) {
            List<String> executeTimes = xxlJobService.nextTriggerTime(cron, time);
            Assert.notEmpty(executeTimes, "不合法的cron表达式,请确认定时任务设定的时间,保存后重试");
            for(int i = 0, size = executeTimes.size(); i < size; i++) {
                try {
                    nextTime = DateUtils.parseDate(executeTimes.get(i), "yyyy-MM-dd HH:mm:ss");
                } catch (Exception e) {
                    log.error(e.getMessage(), e);
                }
                Assert.notNull(nextTime, "非法日期字符串");
                if(nextTime.getTime() - days * 24 * 3600 * 1000 > (System.currentTimeMillis() + 5 * 1000)) {
                    /** fixup: 其中5秒为预读时间，因为xxl-job会将当前到之后5秒内的任务全部取出，例如当前是13:59:55，取出了13:59:55~14:00:00的任务
                     * 但获取下一次执行时间时，发现14:00:00在当前时间13:59:55之后，误认为14:00:00是下次执行的时间，其实14:00:00是这次执行的时间，
                     * 只是xxl-job预读了后5秒的数据，导致任务提前几秒执行了。应用将14:00:00作为下次执行的时间，但xxl-job在13:59:55执行的其实是14:00:00要执行的任务，
                     * 故在14:00:00时，不会再将任务执行一次，导致下次的临时任务时间失效
                     */
                    return new Date(nextTime.getTime() - (int)(days * 24 * 3600 * 1000));
                }
            }
            if(nextTime == null && executeTimes.size() == 1) {
                // 如果是定点任务， 且算上提前时间，任务执行的时间在当前时间之前
                throw new IllegalArgumentException("提前下达的时间已过，该任务将无法执行");
            }
            time <<= 1;
        }
        return null;
    }

    private void voWrapper(MaintainPlanVO vo) {
        if(vo == null) {
            return;
        }
        if(vo instanceof MaintainPlanDetailVO && StringUtils.isNotBlank(vo.getId())) {
            MaintainPlanDetailVO detailVO = (MaintainPlanDetailVO)vo;
            detailVO.setCronTask(xxlJobService.getCronTaskById(vo.getId()));
            detailVO.setDispatch(dispatchConvertor.detailWrapper(detailVO.getDispatch()));
        }
    }

    /**
     * 根据计划生成大修工单
     * @return
     */
    private PlanPackageIssueVO createOverhaulOrder(SessionFactory factory, PlanPackageIssueVO issue) {
        OverhaulOrderBean order = new OverhaulOrderBean();
        List<PlanPackageEquipmentBean> equipments = issue.getEquipments();
        // 大修工单有且仅能有一个设备
        Assert.isTrue(CollectionUtils.size(equipments) == 1, "大修计划有且仅有一台设备");
        PlanPackageEquipmentBean equipment = equipments.get(0);
        order.setEquipmentName(equipment.getEquipmentName());
        order.setEquipmentId(equipment.getEquipmentId());
        order.setDeptManage(issue.getDeptManage());
        order.setDeptManageName(issue.getDeptManageName());
        // 设置标题生成规则: 设备管理单位+内容
        order.setTitle(issue.getDeptManageName() + issue.getContent());
        // 大修工单专业固定为"综合"
        order.setMajor(Constants.Major.SYNTHESIS);
        // 工单由计划生成
        order.setSourceId(Constants.OrderSource.PLAN_ORDER);
        order.setSource(Constants.OrderSource.orderSourceMap.get(order.getSourceId()));
        // 主办单位
        order.setDeptWork(issue.getOrganizer());
        order.setDeptWorkName(issue.getOrganizerName());
        // 工作地点 (取设备的安装位置)
        Map<String, EquipmentParams> equipParamMap = CommonUtils.ok(equipmentFeign.queryById(null, equipment.getEquipmentId()));
        EquipmentParams equipmentParams = equipParamMap.get(Constants.LOCATION_CODE);
        if(!Objects.isNull(equipmentParams) && StringUtils.isNotBlank(equipmentParams.getParamResult())) {
            order.setLocation(equipmentParams.getParamResult());
            order.setLocationName(equipmentParams.getParamValue());
        }
        order.setStartTime(issue.getStartTime());
        order.setEndTime(issue.getEndTime());
        order.setContext(issue.getContent());
        order.setMarks(issue.getDesc());
        // 工单性质(默认为生产类)
        order.setNature(true);
        order.setCreateTime(new Date());
        order.setUpdateTime(new Date());
        order.setCreateBy(issue.getCreateBy());
        order.setCreateByName(issue.getCreateByName());
        order.setUpdateBy(issue.getUpdateBy());
        order.setUpdateByName(issue.getUpdateByName());
        // 设置deptId 和 deptName
        //OrgBean orgBean = CommonUtils.ok(systemFeignService.queryDeptByOrgId(issue.getOrgId()));
        //order.setDeptId(orgBean.getId());
        //order.setDeptName(orgCacheUtils.getOrgNameById(orgBean.getId()));
        order.setDeptId(issue.getOrgId());
        order.setDeptName(issue.getOrgName());
        // 生成的计划工单添加对应的计划id和计划code
        order.setPlanId(issue.getId());
        order.setPlanCode(issue.getPlanCode());
        log.info("大修计划生成大修工单: {}", JSON.toJSONString(order));
        String orderId = CommonUtils.ok(orderFeign.overhaulDraft(order));
        log.info("大修计划生成大修工单, 响应:orderId: {}", orderId);
        Object obj = CommonUtils.ok(orderFeign.overhaulQueryById(orderId));
        log.info("大修计划生成大修工单, 工单详情: {}", obj);
        Assert.notNull(obj, "工单详情查询失败");
        issue.setIssueCode(String.valueOf(((Map)obj).get("code")));
        issue.setIssueId(orderId);
        return issue;
    }

    /**
     * 创建试验工单
     * @param factory 会话
     * @param issue 计划
     * @return 试验工单
     */
    private PlanPackageIssueVO createExperimentOrder(SessionFactory factory, PlanPackageIssueVO issue) {
        ExperimentDTO order = new ExperimentDTO();
        List<PlanPackageEquipmentBean> equipments = issue.getEquipments();
        Assert.notNull(CollectionUtils.size(equipments) > 0, "试验工单设备不能为空");
        order.setDeptManage(issue.getDeptManage());
        order.setDeptManageName(issue.getDeptManageName());
        // todo title默认为 设备管理单位+工作内容
        order.setTitle(issue.getDeptManageName() + issue.getContent());
        // 默认不用不需要生成工单
        order.setNeedStatus(Constants.YesOrNo.NO);
        // 大修工单专业固定为"综合"
        order.setMajor(Constants.Major.SYNTHESIS);
        // 工单由计划生成
        order.setSourceId(Constants.OrderSource.PLAN_ORDER);
        order.setSource(Constants.OrderSource.orderSourceMap.get(order.getSourceId()));
        // 位置取第一个设备的安装位置
        PlanPackageEquipmentBean equipment = equipments.get(0);
        // 工作地点 (取设备的安装位置)
        Map<String, EquipmentParams> equipParamMap = CommonUtils.ok(equipmentFeign.queryById(null, equipment.getEquipmentId()));
        EquipmentParams equipmentParams = equipParamMap.get(Constants.LOCATION_CODE);
        if(!Objects.isNull(equipmentParams) && StringUtils.isNotBlank(equipmentParams.getParamResult())) {
            order.setLocation(equipmentParams.getParamResult());
            order.setLocationName(equipmentParams.getParamValue());
        }
        order.setStartTime(issue.getStartTime());
        order.setEndTime(issue.getEndTime());
        order.setContext(issue.getContent());
        // 工单性质(默认为生产类)
        order.setNature(1);
        order.setMarks(issue.getDesc());
        order.setCreateTime(new Date());
        order.setUpdateTime(new Date());
        order.setCreateBy(issue.getCreateBy());
        order.setCreateByName(issue.getCreateByName());
        order.setUpdateBy(issue.getUpdateBy());
        order.setUpdateByName(issue.getUpdateByName());
        // 设置deptId 和 deptName
        //OrgBean orgBean = CommonUtils.ok(systemFeignService.queryDeptByOrgId(issue.getOrgId()));
        //order.setDeptId(orgBean.getId());
        //order.setDeptName(orgCacheUtils.getOrgNameById(orgBean.getId()));
        order.setDeptId(issue.getOrgId());
        order.setDeptName(issue.getOrgName());
        // 生成的计划工单添加对应的计划id和计划code
        order.setPlanId(issue.getId());
        order.setPlanCode(issue.getPlanCode());
        List<OrderEquipPo> orderEquipPoList = new ArrayList<>();
        equipments.stream().forEach(row -> orderEquipPoList.add(new OrderEquipPo(row.getEquipmentId(), row.getEquipmentName())));
        order.setOrderEquipPoList(orderEquipPoList);
        log.info("试验计划生成试验工单: {}", JSON.toJSONString(order));
        String orderId = CommonUtils.ok(orderFeign.insertOrUpdateExperiment(order));
        log.info("试验计划生成试验工单, 响应:orderId: {}", orderId);
        Object obj = CommonUtils.ok(orderFeign.experimentQueryById(orderId));
        log.info("试验计划生成试验工单, 工单详情: {}", obj);
        Assert.notNull(obj, "工单详情查询失败");
        issue.setIssueCode(String.valueOf(((Map)obj).get("code")));
        issue.setIssueId(orderId);
        return issue;
    }

    /**
     * 创建计划检修工单
     * @param factory
     * @param issue
     * @return
     */
    private PlanPackageIssueVO createRepairOrder(SessionFactory factory, PlanPackageIssueVO issue) {
        RepairDTO order = new RepairDTO();
        List<PlanPackageEquipmentBean> equipments = issue.getEquipments();
        Assert.notNull(CollectionUtils.size(equipments) > 0, "检修计划设备不能为空");
        order.setDeptManage(issue.getDeptManage());
        order.setDeptManageName(issue.getDeptManageName());
        // title默认为 设备管理单位+工作内容
        order.setTitle(issue.getTitle());
        // 默认不用不需要生成工单
        order.setNeedStatus(Constants.YesOrNo.NO);
        // 工单专业固定为"综合"
        order.setMajor(Constants.Major.SYNTHESIS);
        // 工单由计划生成
        order.setSourceId(Constants.OrderSource.PLAN_ORDER);
        order.setSource(Constants.OrderSource.orderSourceMap.get(order.getSourceId()));
        // 位置取第一个设备的安装位置
        PlanPackageEquipmentBean equipment = equipments.get(0);
        // 工作地点 (取设备的安装位置)
        Map<String, EquipmentParams> equipParamMap = CommonUtils.ok(equipmentFeign.queryById(null, equipment.getEquipmentId()));
        EquipmentParams equipmentParams = equipParamMap.get(Constants.LOCATION_CODE);
        if(!Objects.isNull(equipmentParams) && StringUtils.isNotBlank(equipmentParams.getParamResult())) {
            order.setLocation(equipmentParams.getParamResult());
            order.setLocationName(equipmentParams.getParamValue());
        }
        order.setStartTime(issue.getStartTime());
        order.setEndTime(issue.getEndTime());
        order.setContext(issue.getContent());
        // 工单性质(默认为生产类)
        order.setNature(1);
        order.setMarks(issue.getDesc());
        order.setCreateTime(new Date());
        order.setUpdateTime(new Date());
        order.setCreateBy(issue.getCreateBy());
        order.setCreateByName(issue.getCreateByName());
        order.setUpdateBy(issue.getUpdateBy());
        order.setUpdateByName(issue.getUpdateByName());
        // 设置deptId 和 deptName
        //OrgBean orgBean = CommonUtils.ok(systemFeignService.queryDeptByOrgId(issue.getOrgId()));
        //order.setDeptId(orgBean.getId());
        //order.setDeptName(orgCacheUtils.getOrgNameById(orgBean.getId()));
        order.setDeptId(issue.getOrgId());
        order.setDeptName(issue.getOrgName());
        // 生成的计划工单添加对应的计划id和计划code
        order.setPlanId(issue.getId());
        order.setPlanCode(issue.getPlanCode());
        List<OrderEquipPo> orderEquipPoList = new ArrayList<>();
        equipments.stream().forEach(row -> orderEquipPoList.add(new OrderEquipPo(row.getEquipmentId(), row.getEquipmentName())));
        order.setOrderEquipPoList(orderEquipPoList);
        log.info("检修计划生成检修工单: {}", JSON.toJSONString(order));
        String orderId = CommonUtils.ok(orderFeign.insertOrUpdate(order));
        log.info("检修计划生成检修工单, 响应: orderId: {}", orderId);
        Object obj = CommonUtils.ok(orderFeign.repairQueryById(orderId));
        log.info("检修计划生成检修工单, 工单详情: {}", obj);
        Assert.notNull(obj, "工单详情查询失败");
        issue.setIssueCode(String.valueOf(((Map)obj).get("code")));
        issue.setIssueId(orderId);
        return issue;
    }
}
